#include "ComponentFlyAwayBehavior.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentHealth.h"
#include "Components/ComponentCreatureSounds.h"
#include "Components/ComponentCreatureSounds.h"
#include "ComponentPathfinding.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemBodies.h"

#include "SubsystemNoise.h"

#include "GameRandom.hpp"
#include "Engine/MathUtils.hpp"
#include "Log.h"
#include "GameRegistry.hpp"
#include "Managers/BlocksManager.h"
#include "Blocks/WaterBlock.h"
#include "GameSingleton.h"
#include "GameThreadPool.hpp"

#include <iostream>
#include <cfloat>

namespace PocketSurvival
{

    // ************** 【状态机】 **************

    ComponentFlyAwayBehavior::StateMachine::StateMachine(ComponentFlyAwayBehavior *flyAwayBehavior) : m_flyAway(flyAwayBehavior), EasyMachine(MachineState::LookingForDanger) {}

    void ComponentFlyAwayBehavior::StateMachine::enter()
    {
        switch (m_currentState)
        {
        case MachineState::LookingForDanger:
            break;
        case MachineState::DangerDetected:
            m_flyAway->m_importanceLevel = (m_flyAway->componentCreature->componentHealth->health < 0.33f) ? 300.0f : 100.0f;
            m_flyAway->m_nextUpdateTime = 0.0f;
            break;
        case MachineState::RunningAway:
            m_flyAway->componentPathfinding->setDestination(m_flyAway->findSafePlace(), 1.0f, 1.0f, 0, false, true, false, nullptr);
            // m_flyAway->componentCreature->componentCreatureSounds->playPainSound();

            // 广播给其它生物，未完成...
            m_flyAway->subsystemNoise->makeNoise(m_flyAway->componentCreature->componentBody, 0.25f, 6.0f);
            break;
        }
    }

    void ComponentFlyAwayBehavior::StateMachine::update(float dt)
    {
        switch (m_currentState)
        {
        case MachineState::LookingForDanger:
            MACHINE_DELEGATE()
            {
                if (m_flyAway->scanForDanger())
                {
                    changeTo(MachineState::DangerDetected);
                }
            }
            break;
        case MachineState::DangerDetected:
            MACHINE_DELEGATE()
            {
                if (m_flyAway->isActive())
                {
                    changeTo(MachineState::RunningAway);
                    m_flyAway->m_nextUpdateTime = 0.0f;
                }
            }
            break;
        case MachineState::RunningAway:
            MACHINE_DELEGATE()
            {
                Vector3 position;
                m_flyAway->componentCreature->componentBody->getPosition(position);

                if (m_flyAway->isActive() == false || m_flyAway->componentPathfinding->destination == FastVector3::Zero || m_flyAway->componentPathfinding->isStuck)
                {
                    changeTo(MachineState::LookingForDanger);
                }
                else if (m_flyAway->scoreSafePlace(position, m_flyAway->componentPathfinding->destination, nullptr) < 4.0f)
                {
                    m_flyAway->componentPathfinding->setDestination(m_flyAway->findSafePlace(), 1.0f, 0.5f, 0, false, true, false, nullptr);
                }
            }
            break;
        }
    }

    void ComponentFlyAwayBehavior::StateMachine::leave()
    {
        switch (m_currentState)
        {
        case MachineState::LookingForDanger:
            break;
        case MachineState::DangerDetected:
            break;
        case MachineState::RunningAway:
            m_flyAway->m_importanceLevel = 0.0f;
            break;
        }
    }

    // **************************************



    ComponentFlyAwayBehavior::~ComponentFlyAwayBehavior()
    {
        delete m_stateMachine;
    }

    float ComponentFlyAwayBehavior::getImportanceLevel()
    {
        return m_importanceLevel;
    }

    void ComponentFlyAwayBehavior::setActive(bool active)
    {
        m_active = active;
        if (m_active == true)
        {
            m_nextUpdateTime = 0;
        }
    }

    void ComponentFlyAwayBehavior::update(float dt)
    {
        SubsystemTimeOfDay *timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        if (componentCreature->componentHealth->healthChange < 0.0f)
        {
            m_stateMachine->changeTo(MachineState::DangerDetected);
        }
        if (timeOfDay->getGameTime() >= m_nextUpdateTime)
        {
            m_nextUpdateTime = timeOfDay->getGameTime() + GameSingleton::gameRandom->getFloat(0.5f, 1.0f);
            m_stateMachine->update(dt);
        }
    }


    void ComponentFlyAwayBehavior::hearNoise(ComponentBody *sourceBody, const Vector3& sourcePosition, float loudness)
    {
        if (loudness >= 0.25f && m_stateMachine->currentState() != MachineState::RunningAway)
        {
            m_stateMachine->changeTo(MachineState::DangerDetected);
        }
    }

    bool ComponentFlyAwayBehavior::scanForDanger()
    {
        Matrix matrix = componentCreature->componentBody->getMatrix();
        Vector3 translation = matrix.getTranslation();
        std::shared_ptr<Vector3> forward = std::make_shared<Vector3>(matrix.getForward());
        if (scoreSafePlace(translation, translation, forward) < 7.0f)
        {
            return true;
        }
        return false;
    }

    Vector3 ComponentFlyAwayBehavior::findSafePlace()
    {
        SubsystemTerrain *terrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();

        Vector3 position;
        componentCreature->componentBody->getPosition(position);

        float num = -FLT_MAX;
        Vector3 result = position;
        for (int32_t i = 0; i < 20; i++)
        {
            int32_t num2 = Terrain::ToCell(position.posX + GameSingleton::gameRandom->getFloat(-20.0f, 20.0f));
            int32_t num3 = Terrain::ToCell(position.posZ + GameSingleton::gameRandom->getFloat(-20.0f, 20.0f));
            for (int32_t num4 = 255; num4 >= 0; num4--)
            {
                int32_t cellContents = terrain->getCellContents(num2, num4, num3);
                if (GameSingleton::blocksManager->blocks[cellContents]->IsCollidable || cellContents == WaterBlock::Index())
                {
                    Vector3 vector(num2 + 0.5f, num4 + 1.1f, num3 + 0.5f);
                    float num5 = scoreSafePlace(position, vector, nullptr);
                    if (num5 > num)
                    {
                        num = num5;
                        result = vector;
                    }
                    break;
                }
            }
        }
        return result;
    }

    float ComponentFlyAwayBehavior::scoreSafePlace(const Vector3 &currentPosition, const Vector3 &safePosition, std::shared_ptr<Vector3> lookDirection)
    {
        float num = 16.0f;
        Vector3 position;
        componentCreature->componentBody->getPosition(position);

        m_componentBodyVec.clear();
        GameSingleton::singletonPtr->getSingleton<SubsystemBodies>()->findBodiesAroundPoint(Vector2(position.posX, position.posZ), 16.0f, m_componentBodyVec);
        for (ComponentBody * body : m_componentBodyVec)
        {
            if (body->componentCreature != nullptr && false == isPredator(body->componentCreature))
            {
                continue;
            }

            Vector3 bodyPosition;
            body->getPosition(bodyPosition);

            Vector3 v = safePosition - bodyPosition;
            if (nullptr == lookDirection || 0.0f - Vector3::Dot(*lookDirection, v) > 0.0f)
            {
                if (v.posY >= 4.0f)
                {
                    v *= 2.0f;
                }
                num = MathUtils::Min(num, v.length());
            }
        }
        float num2 = Vector3::Distance(currentPosition, safePosition);
        if (num2 < 8.0f)
        {
            // Log::Info(fmt::format("接近, num2: {}", num2));
            return num * 0.5f;
        }
        return num * MathUtils::Lerp(1.0f, 0.75f, MathUtils::Saturate(num2 / 20.0f));
    }


    bool ComponentFlyAwayBehavior::isPredator(ComponentCreature *creature)
    {
        if (creature->entity != entity)
        {
            if (creature->category == CreatureCategory::LandPredator || creature->category == CreatureCategory::WaterPredator || creature->category == CreatureCategory::LandOther)
            {
                return true;
            }
        }
        return false;
    }

    void ComponentFlyAwayBehavior::collidedWithBodyEvent(ComponentBody *body)
    {
        Log::Info("collidedWithBodyEvent");
        if (m_stateMachine->currentState() != MachineState::RunningAway)
        {
            m_stateMachine->changeTo(MachineState::DangerDetected);
        }
    }

    static const std::string componentName = "FlyAwayBehavior";
    const std::string &ComponentFlyAwayBehavior::getName() const
    {
        return componentName;
    }
    const std::string &ComponentFlyAwayBehavior::GetName()
    {
        return componentName;
    }
    void ComponentFlyAwayBehavior::load(const nlohmann::json &jsonData)
    {
        componentCreature = GameSingleton::gameRegistry->get<ComponentCreature *>(entity);
        componentPathfinding = GameSingleton::gameRegistry->get<ComponentPathfinding *>(entity);

        subsystemNoise = GameSingleton::singletonPtr->getSingleton<SubsystemNoise>();

        componentCreature->componentBody->addCollidedWithBodyEvent(std::bind(&ComponentFlyAwayBehavior::collidedWithBodyEvent, this, std::placeholders::_1));

        m_stateMachine = new StateMachine(this);
    }

}
